package com.cdg.polymeric.core.ext.excutor;

import com.cdg.polymeric.BeanFactoryUtils;
import com.cdg.polymeric.Excutor;
import com.cdg.polymeric.ReflectUtils;
import com.cdg.polymeric.config.AnnotationSetting;
import com.cdg.polymeric.config.ClassAnnotationProperties;
import com.cdg.polymeric.config.ClassAnnotationSetting;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.apache.ibatis.reflection.Reflector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

public abstract class AbstactFeignTemplateExcutor implements Excutor {
    private static Logger logger = LoggerFactory.getLogger(AbstactFeignTemplateExcutor.class);
    private Properties properties;
    private AnnotationSetting annotationSetting;
    private Object mybatisQueryResult = null;
    private Reflector reflector = null;
    public static LoadingCache<String, Object> caches;

    public void setReflector(Reflector reflector) {
        this.reflector = reflector;
    }

    public void setMybatisQueryResult(Object mybatisQueryResult) {
        this.mybatisQueryResult = mybatisQueryResult;
    }

    public AbstactFeignTemplateExcutor(AnnotationSetting annotationSetting,Properties properties){
        this.annotationSetting = annotationSetting;
        caches = CacheBuilder.newBuilder()
                                  .maximumSize((Integer) properties.get("guavaCacheNumMaxSize"))
                                  .refreshAfterWrite((Integer) properties.get("guavaCacheRefreshWriteTime"), TimeUnit.MINUTES)
                                  .build(new CacheLoader<String, Object>() {

                                       @Override
                                       public Object load(String s) throws Exception {
                                           Object cache = caches.getIfPresent(s);
                                           return Objects.nonNull(cache) ? cache : null;
                                       }
                                  });

    }



    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    @Override
    public synchronized Object join() {
        AtomicReference<Object> data = new AtomicReference<>();
        if (this.annotationSetting != null || properties != null){
            // 注解标记在了类上 需要组合字段属性 做缓存 处理polymeric 逻辑
            if (this.annotationSetting instanceof ClassAnnotationSetting){
                ClassAnnotationSetting classAnnotationSetting = (ClassAnnotationSetting)annotationSetting;
                List<ClassAnnotationProperties> classAnnotationProperties = classAnnotationSetting.getFieldSetting();
                final List<ClassAnnotationProperties> fieldCachedSettings = classAnnotationSetting.getFieldCacheSetting();
                Map result = new HashMap();
                classAnnotationProperties.stream().forEach(c->{
                    c.setEnvironment(classAnnotationSetting.getEnvironment());
                });
                //匹配 是否配置了缓存项
                boolean cacheEnable = classAnnotationProperties.stream().anyMatch(ClassAnnotationProperties::isEnableCache);
                if (cacheEnable){
                    // 判断已经配置了 缓存配置的 是否已经都被缓存
                    boolean isAllCached = fieldCachedSettings.stream().allMatch(ClassAnnotationProperties::isCachedSelf);
                    // 都被缓存了 从缓存中取 否则再查询一次 远程数据源
                    if (isAllCached){
                        for (ClassAnnotationProperties classAnnotationProp : fieldCachedSettings){
                            String key = reflector.getType() + classAnnotationProp.getName();
                            Object cacheData = caches.getIfPresent(key);
                            result.putIfAbsent(classAnnotationProp.getName(),cacheData);

                        }

                        return result;

                    }
                    // 执行远程查询 并放进缓存
                    LinkedHashMap map = (LinkedHashMap) excute(data).get();
                    for (ClassAnnotationProperties classAnnotationProp : fieldCachedSettings){
                        caches.put(reflector.getType() + classAnnotationProp.getName(),map.get(classAnnotationProp.getName()));
                    }

                    return map;


                }else {
                    // 执行远程查询
                    return excute(data).get();
                }



            }

            // 处理 polymericField 逻辑
            if (annotationSetting.isEnableCache()){
                String key = reflector.getType().getName() + annotationSetting.getFieldName();
                Object cache = caches.getIfPresent(key);
                if (null == cache){
                    caches.put(key,excute(data).get());
                    return excute(data).get();
                }else {
                    return cache;
                }
            }else{
              return excute(data).get();

            }

        }

        return null;

    }



    public AtomicReference<Object> excute(AtomicReference<Object> reference){
        Object bean = BeanFactoryUtils.getBean(annotationSetting.getFeign());
        try {
            Method method = annotationSetting.getFeign().getMethod(annotationSetting.getMethod(),annotationSetting.getArgs());
            ArrayList queryObjects = (ArrayList) mybatisQueryResult;
            CopyOnWriteArrayList safetyList = new CopyOnWriteArrayList(queryObjects);
            if (safetyList != null){
                safetyList.parallelStream().forEach(q->{
                    Class clazz = reflector.getType();
                    Map<String,Object> valueMap = ReflectUtils.getFieldValue(clazz,q);
                    if (StringUtils.isEmpty(annotationSetting.getKey())){
                        // 默认为id
                        Object id = valueMap.get("id");
                        if(id != null){
                            try {
                                reference.set(ReflectUtils.invokeMethod(bean, method.getName(), id));
                            } catch (InvocationTargetException | IllegalAccessException e) {
                                e.printStackTrace();
                            }
                            annotationSetting.setTargetType(reference.get().getClass());
                        }else{
                            logger.error("polymeric entity missing id attribute");
                        }
                    }else {
                        //  通过指定 key 查询
                        String key = annotationSetting.getKey();
                        Object keyObject = valueMap.get(key);
                        try {
                            reference.set(ReflectUtils.invokeMethod(bean, method.getName(), keyObject));
                        } catch (InvocationTargetException | IllegalAccessException e) {
                            e.printStackTrace();
                        }
                        annotationSetting.setTargetType(reference.get().getClass());
                    }
                });
            }


        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } finally {
            return reference;

        }
    }





    public AnnotationSetting getAnnotationSetting() {
        return annotationSetting;
    }

    public Object getMybatisQueryResult() {
        return mybatisQueryResult;
    }


}
